//#define USE_DUNGEONBUDDY_DLL
using System.Collections.Generic;
using System.Linq;
using CommonBehaviors.Actions;
using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.Helpers;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.TreeSharp;
#if USE_DUNGEONBUDDY_DLL
using Bots.DungeonBuddyDll;
using Bots.DungeonBuddyDll.Attributes;
using Bots.DungeonBuddyDll.Helpers;

namespace Bots.DungeonBuddyDll.Dungeon_Scripts.Burning_Crusade
#else
    using Bots.DungeonBuddy.Attributes;
    using Bots.DungeonBuddy.Helpers;
namespace Bots.DungeonBuddy.Dungeon_Scripts.Burning_Crusade
#endif
{
    public class TheEscapeFromDurnholde : Dungeon
    {
        #region Overrides of Dungeon

        private const uint ThrallId = 17876;

        public override uint DungeonId
        {
            get { return 170; }
        }

        public override WoWPoint Entrance
        {
            get { return new WoWPoint(-8332.569, -4057.429, -207.7462); }
        }

        public override WoWPoint ExitLocation
        {
            get { return new WoWPoint(2762.708, 1294.983, 13.77185); }
        }

        public override void RemoveTargetsFilter(List<WoWObject> units)
        {
            WoWUnit thrall = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == ThrallId);
            units.RemoveAll(
                u =>
                { // remove low level units.
                    var unit = u as WoWUnit;
                    if (unit == null)
                        return false;

                    if (StyxWoW.Me.Level - unit.Level > 5 && !unit.Combat)
                        return true;

                    if (thrall != null && unit.DistanceSqr > 25 * 25 && !unit.Combat)
                        return true;
                    return false;
                });
        }

        public override void IncludeTargetsFilter(List<WoWObject> incomingunits, HashSet<WoWObject> outgoingunits)
        {
            foreach (var u in incomingunits)
            {
                var unit = u as WoWUnit;
                var currentTarget = unit.CurrentTarget;
                if (currentTarget != null && currentTarget.Entry == ThrallId)
                    outgoingunits.Add(unit);
            }
        }

        public override void WeighTargetsFilter(List<Targeting.TargetPriority> units)
        {
            foreach (var priority in units)
            {
                var unit = priority.Object as WoWUnit;
                if (unit == null) continue;
                var currentTarget = unit.CurrentTarget;
                if (currentTarget != null && currentTarget.Entry == ThrallId)
                    priority.Score += 250;
                else if (unit.Entry == DurnholdeRiflemanId && StyxWoW.Me.IsRange())
                    priority.Score += 200;

                else if (unit.Entry == DurnholdeWardenId && Me.IsDps())
                    priority.Score += 210;
            }
        }

        public override void OnEnter()
        {
            _epochHunterStep1Done = _epochHunterStep2Done = _epochHunterStep3Done = _villageOnFire = false;
        }

        #endregion

        private const uint DurnholdeRiflemanId = 17820;
        private const uint DurnholdeWardenId = 17833;

        private readonly CircularQueue<WoWPoint> _barrelLocations = new CircularQueue<WoWPoint>
                                                                    {
                                                                        new WoWPoint(2102.327, 113.4871, 53.25289),
                                                                        new WoWPoint(2166.457, 219.0035, 52.66649),
                                                                    };

        private readonly WoWPoint[] _tunnelPath = new[]
                                                  {
                                                      new WoWPoint(2748.281, 1305.534, 14.03836), new WoWPoint(2699.57, 1320.918, 14.05938),
                                                      new WoWPoint(2608.236, 1215.956, 13.98584), new WoWPoint(2489.021, 1234.487, 46.98243),
                                                      new WoWPoint(2455.663, 1255.215, 58.72845), new WoWPoint(2402.185, 1226.987, 62.8508),
                                                      new WoWPoint(2378.912, 1174.472, 65.89436)
                                                  };

        private readonly WoWPoint _villageLocation = new WoWPoint(2127.658, 180.1258, 69.28806);

        private LocalPlayer Me
        {
            get { return StyxWoW.Me; }
        }

        private bool IsInTunnel
        {
            get
            {
                var myLoc = Me.Location;
                for (int i = 1; i < _tunnelPath.Length; i++)
                {
                    var start = _tunnelPath[i - 1];
                    var end = _tunnelPath[i];
                    var nearestPoint = myLoc.GetNearestPointOnSegment(start, end);
                    if (myLoc.Distance(nearestPoint) < 40)
                        return true;
                }
                return false;
            }
        }

        private bool OnFire
        {
            get { return ObjectManager.GetObjectsOfType<WoWGameObject>().Any(o => o.IsValid && o.Entry == 182592); }
        }

        [EncounterHandler(0)]
        public Composite RootBehavior()
        {
            return
                new PrioritySelector(
                    new Decorator(
                        ctx => IsInTunnel && _tunnelPath[_tunnelPath.Length - 1].Distance(Me.Location) > 10,
                        new Action(ctx => Navigator.MoveTo(_tunnelPath[_tunnelPath.Length - 1]))));
        }

        [EncounterHandler(18723, "Erozion", Mode = CallBehaviorMode.Proximity)]
        public Composite ErozionEncounter()
        {
            const int drakeId = 18725;
            WoWUnit unit = null;
            return new PrioritySelector(
                ctx => unit = ctx as WoWUnit,
                // pickup quests
                new Decorator(
                    ctx => !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(unit.Location) && unit.QuestGiverStatus == QuestGiverStatus.Available,
                    ScriptHelpers.CreatePickupQuest(() => unit)),
                // turnin quests.
                new Decorator(
                    ctx => !Me.Combat && !ScriptHelpers.WillPullAggroAtLocation(unit.Location) && unit.QuestGiverStatus == QuestGiverStatus.TurnIn,
                    ScriptHelpers.CreateTurninQuest(() => unit)),
                // get some bombs.
                new Decorator(ctx => !StyxWoW.Me.BagItems.Any(i => i.IsValid && i.Entry == BombItemId), ScriptHelpers.CreateTalkToNpc(() => unit)),
                // talk to the drake for a ride
                new Decorator(ctx => StyxWoW.Me.Mounted, new Action(ctx => Mount.Dismount())),
                ScriptHelpers.CreateTalkToNpc(drakeId));
        }

        [ObjectHandler(184393, "Prison Door")]
        public Composite PrisonDoorHandler()
        {
            return new PrioritySelector();
        }

        [EncounterHandler(17862, "Captain Skarloc", Mode = CallBehaviorMode.CurrentBoss)]
        public Composite CaptainSkarlocEncounter()
        {
            var thrallLoc = new WoWPoint(2230.14, 117.0324, 82.29137);
            var thrallEscortEndLoc = new WoWPoint(2063.042, 229.1765, 64.49027);
            WoWUnit thrall = null;
            return
                new PrioritySelector(ctx => thrall = Thrall,
                    new Decorator(
                        ctx => Me.Location.Distance(thrallLoc) < 10 && !Me.Combat && thrall == null,
                        new Action(ctx => ScriptHelpers.MarkBossAsDead("Captain Skarloc", "Thrall wasn't found in his cage"))),
                    new Decorator(
                        ctx => Me.PartyMembers.All(g => g.IsAlive),
                        ScriptHelpers.CreateTankTalkToThenEscortNpc((int)ThrallId, 0, thrallLoc, thrallEscortEndLoc, 10, () => thrall != null && thrall.Location.DistanceSqr(thrallEscortEndLoc) <= 5 * 5 && thrall.CanGossip)));
        }

        #region Epoch Hunter

        private bool _epochHunterStep1Done;
        private bool _epochHunterStep2Done;
        private bool _epochHunterStep3Done;

        private WoWUnit _taretha;
        private WoWUnit _thrall;

        private WoWUnit Thrall
        {
            get { return ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == ThrallId); }
        }

        [EncounterHandler(18096, "Epoch Hunter", Mode = CallBehaviorMode.CurrentBoss)]
        public Composite EpochHunterSpawnBehavior()
        {
            var thrallStartLoc1 = new WoWPoint(2063.042, 229.1765, 64.49027);
            var thrallStartLoc2 = new WoWPoint(2486.878, 624.3096, 57.90615);
            var thrallStartLoc3 = new WoWPoint(2660.485, 659.4092, 61.9358);
            var thrallEndLoc = new WoWPoint(2635.069, 673.2795, 54.46173);

            WoWUnit epochHunter = null;

            return new Decorator(
                ctx => Me.IsTank(),
                new PrioritySelector(
                    ctx =>
                    {
                        _thrall = Thrall;
                        epochHunter = ctx as WoWUnit;
                        _taretha = ObjectManager.GetObjectsOfType<WoWUnit>().FirstOrDefault(u => u.Entry == 18887);
                        if (!_epochHunterStep1Done && _thrall != null)
                            _epochHunterStep1Done = _thrall.Location.DistanceSqr(thrallStartLoc2) < 5 * 5 || _epochHunterStep2Done;
                        if (!_epochHunterStep2Done && _thrall != null)
                            _epochHunterStep2Done = _thrall.Location.DistanceSqr(thrallStartLoc3) < 5 * 5 && _taretha != null && _taretha.CanGossip;

                        if (!_epochHunterStep3Done && _thrall != null)
                            _epochHunterStep3Done = _epochHunterStep2Done && (_thrall.Location.DistanceSqr(thrallEndLoc) < 5 * 5 || epochHunter != null &&  epochHunter.Combat);

                        if (_thrall != null && _thrall.IsDead)
                            _epochHunterStep2Done = false;

                        return epochHunter;
                    },
                    new Decorator(
                        ctx => !_epochHunterStep1Done,
                        new PrioritySelector(
                            new Decorator(ctx => StyxWoW.Me.Location.DistanceSqr(thrallStartLoc1) < 5 * 5 && _thrall == null, new Action(ctx => _epochHunterStep1Done = true)),
                            new Decorator(ctx => Targeting.Instance.IsEmpty(),
                                ScriptHelpers.CreateMountBehavior(() => thrallStartLoc2)),
                            ScriptHelpers.CreateTankTalkToThenEscortNpc((int)ThrallId, 0, thrallStartLoc1, thrallStartLoc2, 10, () => true))),
                    new Decorator(
                        ctx => !_epochHunterStep2Done && _epochHunterStep1Done,
                        ScriptHelpers.CreateTankTalkToThenEscortNpc((int)ThrallId, 0, thrallStartLoc2, thrallStartLoc3, 3, () => _taretha != null && _taretha.CanGossip)),
                    new Decorator(
                        ctx => !_epochHunterStep3Done && _epochHunterStep2Done && _epochHunterStep1Done,
                        new PrioritySelector(
                            new Decorator(ctx => StyxWoW.Me.Location.DistanceSqr(thrallStartLoc3) < 5 * 5 && _thrall == null, new Action(ctx => _epochHunterStep2Done = false)),
                            new Decorator(
                                ctx => _taretha != null && _taretha.CanGossip,
                                new Sequence(ScriptHelpers.CreateTalkToNpcContinue(18887), ScriptHelpers.CreateTalkToNpcContinue(18887))),
                            new Decorator(
                                ctx => _taretha != null && !_taretha.CanGossip,
                                ScriptHelpers.CreateTankTalkToThenEscortNpc((int)ThrallId, 0, thrallStartLoc3, thrallEndLoc, 3, () => epochHunter.Combat))))));
        }

        [EncounterHandler(18096, "Epoch Hunter")]
        public Composite EpochHunterEncounter()
        {
            WoWUnit epochHunter = null;
            return new PrioritySelector(
                ctx => epochHunter = ctx as WoWUnit,
                ScriptHelpers.CreateAvoidUnitAnglesBehavior(
                    ctx => !Me.IsTank() && epochHunter.CurrentTargetGuid != Me.Guid && epochHunter.Distance < 20, () => epochHunter, new ScriptHelpers.AngleSpan(0, 180)),
                new Decorator(ctx => Me.IsTank() && Targeting.Instance.TargetList.All(t => t.CurrentTargetGuid == Me.Guid), ScriptHelpers.CreateTankFaceAwayGroupUnit(20)));
        }

        #endregion

        #region Lieutenant Drake

        private const uint BombItemId = 25853;
        private const uint LieutenantDrakeId = 17848;
        private bool _villageOnFire;

        [EncounterHandler(17848, "Lieutenant Drake", Mode = CallBehaviorMode.CurrentBoss)]
        public Composite LieutenantDrakeSpawnEncounter()
        {
            WoWUnit boss = null;
            WoWGameObject barrel = null;
            return new PrioritySelector(
                ctx =>
                {
                    if (!_villageOnFire)
                        _villageOnFire = OnFire;
                    return boss = ctx as WoWUnit;
                },
                // go to each barrel
                new Decorator(
                    ctx => Targeting.Instance.IsEmpty() && !_villageOnFire && Me.Location.Distance(_villageLocation) <= 200,
                    new PrioritySelector(
                        ctx =>
                        {
                            barrel =
                                ObjectManager.GetObjectsOfType<WoWGameObject>().Where(o => o.Entry == 182589 && o.CanUse()).OrderBy(o => o.DistanceSqr).FirstOrDefault();
                            return _barrelLocations.Peek();
                        },
                        new ActionSetActivity("Blowing up barrels"),
                        new Decorator(ctx => barrel != null && barrel.Distance > 10, new Action(ctx => ScriptHelpers.MoveTankTo(barrel.Location))),
                        new Decorator(
                            ctx => barrel != null && barrel.Distance <= 10 && !barrel.InUse && !Me.Combat && StyxWoW.Me.BagItems.Any(i => i.IsValid && i.Entry == BombItemId),
                            ScriptHelpers.CreateInteractWithObject(() => barrel, 6)),
                        new Decorator(
                            ctx => barrel == null,
                            new PrioritySelector(
                                new Decorator(ctx => StyxWoW.Me.Location.Distance2DSqr((WoWPoint)ctx) <= 3 * 3, new Action(ctx => _barrelLocations.Dequeue())),
                                new Action(ctx => ScriptHelpers.MoveTankTo((WoWPoint)ctx)))))));
        }

        [EncounterHandler(17848, "Lieutenant Drake")]
        public Composite LieutenantDrakeEncounter()
        {
            AddAvoidObject(ctx => !Me.IsTank(), 8, o => o.Entry == LieutenantDrakeId && o.ToUnit().HasAura("Whirlwind"));
            return new PrioritySelector();
        }

        #endregion
    }
}